// smlog.cpp : Defines the entry point for the DLL application.
//
#include <windows.h>
#include "smlog.h"

debugServerContext *smlog_server_context = NULL;

BOOL APIENTRY DllMain( HANDLE hModule, 
                       DWORD  ul_reason_for_call, 
                       LPVOID lpReserved
					 )
{
	switch (ul_reason_for_call)
	{
	case DLL_PROCESS_ATTACH:
	case DLL_THREAD_ATTACH:
        break;
	case DLL_THREAD_DETACH:
	case DLL_PROCESS_DETACH:
        releaseAllSharedMemoryResources(smlog_server_context);
		break;
	}
    return TRUE;
}

int smlog_init(int timeout, debugServerContext **smlog_server_context)
{
    int maxSize;
    int result = 0;
    char buff[255] = {0};

    if( smlog_server_context == NULL  )
        return SMLOG_SM_ERR_INVALID_PARAMETER;

    *smlog_server_context = (debugServerContext *)calloc(1, sizeof(debugServerContext));
    if( *smlog_server_context == NULL )
        return SMLOG_SM_ERR_NO_MEMORY;

    if( timeout < 0 )
        timeout = SMLOG_SM_DEFAULT_TIMEOUT;
    (*smlog_server_context)->timeout = timeout;

    (*smlog_server_context)->fileMap
                = OpenFileMapping(FILE_MAP_ALL_ACCESS, TRUE, SMLOG_SM_NAME);
    if( (*smlog_server_context)->fileMap == NULL )
    {
        //Could not create the mapping
        //Should GetLastError() and SetLastError()
        return SMLOG_SM_ERR_NO_SHARED_MEMORY;
    }

    (*smlog_server_context)->data
        = (debugServerCommunicationData *)MapViewOfFile((*smlog_server_context)->fileMap,
                  FILE_MAP_ALL_ACCESS, 0, 0, sizeof(debugServerCommunicationData));
    if( (*smlog_server_context)->data == NULL )
    {
        //Could not create map view
        //Should GetLastError()

        CloseHandle((*smlog_server_context)->fileMap);
        return SMLOG_SM_ERR_GENERAL_ERROR;
    }

    maxSize = (*smlog_server_context)->data->maxMemSize;
    if( maxSize > SMLOG_SM_MEM_MAX || maxSize < SMLOG_SM_MEM_MIN )
    {
        //invalid max size

        CloseHandle((*smlog_server_context)->fileMap);
        return SMLOG_SM_ERR_GENERAL_ERROR;
    }
    UnmapViewOfFile( (LPCVOID)(*smlog_server_context)->data );

    (*smlog_server_context)->data
        = (debugServerCommunicationData *)MapViewOfFile((*smlog_server_context)->fileMap,
                  FILE_MAP_ALL_ACCESS, 0, 0, maxSize);
    if( (*smlog_server_context)->data == NULL )
    {
        //Could not create map view
        //Should GetLastError()

        CloseHandle((*smlog_server_context)->fileMap);
        return SMLOG_SM_ERR_GENERAL_ERROR;
    }

    (*smlog_server_context)->mutex 
                = OpenMutex((MUTEX_ALL_ACCESS|MUTEX_MODIFY_STATE) & ~WRITE_DAC, 
                                                        TRUE, SMLOG_SM_MUTEX_NAME);
    if( (*smlog_server_context)->mutex == NULL )
    {
        //Could not open mutex
        FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS,
                        NULL, GetLastError(), 0, (LPTSTR) &buff, 254, NULL );
        UnmapViewOfFile( (LPCVOID)(*smlog_server_context)->data );
        CloseHandle((*smlog_server_context)->fileMap);
        return SMLOG_SM_ERR_GENERAL_ERROR;
    }
 
    (*smlog_server_context)->serverEvent 
        = OpenEvent((EVENT_ALL_ACCESS|EVENT_MODIFY_STATE) & ~WRITE_DAC, 
                                                FALSE, SMLOG_SM_SERVEREVENT_NAME);
    if( (*smlog_server_context)->serverEvent == NULL )
    {
        //Could not create or open smlog_server_context event
        UnmapViewOfFile( (LPCVOID)(*smlog_server_context)->data );
        CloseHandle((*smlog_server_context)->fileMap);
        return SMLOG_SM_ERR_GENERAL_ERROR;
    }

    (*smlog_server_context)->clientEvent 
        = OpenEvent((EVENT_ALL_ACCESS|EVENT_MODIFY_STATE) & ~WRITE_DAC,
                                                FALSE, SMLOG_SM_CLIENTEVENT_NAME);
    if( (*smlog_server_context)->clientEvent == NULL )
    {
        //Could not create or open smlog_server_context event
        UnmapViewOfFile( (LPCVOID)(*smlog_server_context)->data );
        CloseHandle((*smlog_server_context)->serverEvent);
        CloseHandle((*smlog_server_context)->fileMap);
        return SMLOG_SM_ERR_GENERAL_ERROR;
    }

    return result;
}

/********************************************************************************
**  Basic synchronization algorithm for interacting with the debug server.
**  One mutex and two events are used.
**
** (i)  SERVER -> Initialize memory map buffer
** (ii) SERVER -> Wait for CLIENT event
** (iii)CLIENT -> If ready to write, then wait for MUTEX
** (iv) CLIENT -> On getting MUTEX, write
** (v)  CLIENT -> Signal CLIENT event when done writing
** (vi) CLIENT -> Wait on SERVER event
** (vii)SERVER -> Read then clear buffer
** (iix)SERVER -> Signal SERVER event
** (ix) CLIENT -> If not done writing, then go back to step "(iv)"
** (x)  CLIENT -> Release MUTEX
**
********************************************************************************/

int smlog_write( debugServerContext *smlog_server_context, wchar_t *str )
{
    int waitResult;
    size_t charsWritten    = 0;
    size_t strLength       = 0;
    size_t maxChars;
    int result;
    char buff[255] = {0};

    if( smlog_server_context == NULL )
         return SMLOG_SM_ERR_INVALID_PARAMETER;

    strLength = wcslen(str);
    if( strLength < 1 )
        return SMLOG_SM_ERR_INVALID_PARAMETER;

    /* wait for MUTEX */
    waitResult = WaitForSingleObject(smlog_server_context->mutex, 
                                                smlog_server_context->timeout);
    if( waitResult == WAIT_FAILED )
    {
        FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS,
                        NULL, GetLastError(), 0, (LPTSTR) &buff, 254, NULL );
        return SMLOG_SM_ERR_INVALID_PARAMETER;
    }

    if( waitResult == WAIT_TIMEOUT )
        return SMLOG_SM_ERR_MUTEX_TIMEOUT;

    /* write to the buffer */
    maxChars = smlog_server_context->data->maxMemSize/sizeof(wchar_t)-1;
    do 
    {
        strLength = wcslen(str);
        if( strLength >= maxChars )
            charsWritten = maxChars-1;
        else charsWritten = strLength;

        smlog_server_context->data->dataSize = (DWORD)((charsWritten+1)*sizeof(wchar_t));
        smlog_server_context->data->dataType = SMLOG_SM_DATATYPE_STRING;
        wcsncpy_s(&(smlog_server_context->data->data),  
            smlog_server_context->data->maxMemSize, str, charsWritten);

        /* signal CLIENT event */
        result = SetEvent(smlog_server_context->clientEvent);

        /* wait on SERVER event */
        waitResult = WaitForSingleObject(smlog_server_context->serverEvent, 
                                                    smlog_server_context->timeout);
        if( waitResult == WAIT_TIMEOUT )
            return SMLOG_SM_ERR_SERVER_EVENT_TIMEOUT;

        str += charsWritten;
    }
    while( charsWritten >= maxChars-1 );

    /* Release MUTEX */
    result = ReleaseMutex(smlog_server_context->mutex);

    return SMLOG_SM_SUCCESS;
}

void releaseAllSharedMemoryResources(debugServerContext *smlog_server_context)
{
    if(smlog_server_context == NULL)
        return;

    if(smlog_server_context->data)
        UnmapViewOfFile( (LPCVOID)(smlog_server_context->data) );

    if(smlog_server_context->fileMap)
        CloseHandle(smlog_server_context->fileMap);

    if(smlog_server_context->mutex)
        CloseHandle(smlog_server_context->mutex);

    if(smlog_server_context->clientEvent)
        CloseHandle(smlog_server_context->clientEvent);

    if(smlog_server_context->serverEvent)
        CloseHandle(smlog_server_context->serverEvent);

    free(smlog_server_context);
}